"""
 VarGenerator module allows to parse C++ variable declarations like

    dsVECTOR<dsSTRING> array; // comment
    ds::MAP<dsSTRING, bool>::iterator iter; /* block comment */
    int index = 10;
    string str = "Default value";
"""

from S3D import *
from S3D.AutoTagSearcher import Callback
from S3D.Parser import Parser, ParserData
from S3D.MultiGenerator.Generators.MacrosGenerator import MacrosGenerator
from S3D.Decorators import singleton

@singleton
class VarGenerator(Callback):
    def register(self, searcher):
        searcher.addGenerator("var", self)

    def begin(self, info):
        self.map = {}

    def end(self, info):
        self.map = None

    def input(self, params, text, info):
        if len(params) != 1:
            info.addError("'desc': single parameter with name expected")
            return

        name = params[0]
        if name in self.map.keys():
            info.addError("'%s': redifinition" % name)
            info.addErrorInfo("see previous declaraion", self.map[name].file, self.map[name].line)
            return

        c = VarCompiler()

        value = ParserData(
            vars = c.compile(text, info.file, info.line),
            file = info.file,
            line = info.line)

        if vars == None:
            info.errorMsg += c.errorMsg
            info.errorsCount += c.errorsCount
            return

        self.map[name] = value

    def output(self, params, info):
        if len(params) < 2:
            info.addError("'var': desc and macros names were expected")
            return

        name = params[0]
        if name not in self.map.keys():
            info.addError("'%s': unknown variables definition" % name)
            return

        ctxt = self.map[name]

        locals = {
            "vars":     ctxt.vars,
            "ctxt":     ctxt,
        }

        return MacrosGenerator().outputEx(params[1:], info, locals)

class Var:
    def __init__(self, type, name, defVal):
        self.type = type
        self.name = name
        self.defVal = defVal

    def __str__(self):
        if self.defVal is None:
            return "(%s %s)" % (self.type, self.name)
        return "(%s %s = %s)" % (self.type, self.name, self.defVal)

    def __repr__(self):
        return str(self)

class Type:
    NAME    = 0         # name
    SUBNAME = 1         # <subtype>::name
    ARGS    = 2         # <subtype><args[0], ...> (like dsVECTOR<dsSTRING>)

    def __init__(self, type):
        self.type = type

    def __str__(self):
        if self.type == Type.NAME:
            return self.name
        if self.type == Type.SUBNAME:
            return "%s.%s" % (self.subtype, self.name)
        if self.type == Type.ARGS:
            return "%s%s" % (self.subtype, self.args)
        return "<invalid type>"

    def __repr__(self):
        return str(self)

    def buildTypeName(self):
        if self.type == Type.NAME:
            return BuildName(self.name)
        if self.type == Type.SUBNAME:
            return BuildSubname(self.subtype.buildTypeName(), self.name)
        if self.type == Type.ARGS:
            return BuildTemplateArgs(self.subtype.buildTypeName(), [p.buildTypeName() for p in self.args])
        return str(self)

    def createName(name):
        res = Type(Type.NAME)
        res.name = name
        return res

    def createSubname(type, name):
        res = Type(Type.SUBNAME)
        res.subtype = type
        res.name = name
        return res

    def createArgs(type, args):
        res = Type(Type.ARGS)
        res.subtype = type
        res.args = args
        return res

class VarCompiler(Parser):
    def parse(self):
        self.vars = []
        
        while not self.nextIsToken('eof'):
            self.parseVar()

        return self.vars

    def parseVar(self):
        type = self.parseType()
        name = self.matchWord()

        defVal = None
        if self.nextIsSymbol('=', match=True):
            defVal = self.matchValue()

        self.matchSymbol(";")
        
        self.vars.append(Var(type, name, defVal))

    def parseType(self):
        name = self.matchWord()
        type = Type.createName(name)
        type = self.parseTypeArgs(type)

        while self.nextIsOperator('::', match=True):
            name = self.matchWord()
            type = Type.createSubname(type, name)
            type = self.parseTypeArgs(type)

        return type
    
    def parseTypeArgs(self, type):
        if not self.nextIsSymbol('<', match=True):
            return type

        args = [self.parseType()]

        while not self.nextIsSymbol('>'):
            self.matchSymbol(',')
            args.append(self.parseType())

        self.matchSymbol('>')

        return Type.createArgs(type, args)
